#include <iostream>
#include <fstream>

#include <opencv2/opencv.hpp>
//#include <opencv2/core.hpp>
//#include <opencv2/imgproc.hpp>
//#include <opencv2/highgui.hpp>
#define w 400

using namespace std;
using namespace cv;

void MyEllipse( Mat img, double angle );
void MyFilledCircle( Mat img, Point center );
void MyPolygon( Mat img );
void MyLine( Mat img, Point start, Point end );

static inline void show(const string &title, const cv::Mat &img) {
    cv::namedWindow(title);
    cv::imshow(title, img);
}

static void show_mat_param(const cv::Mat &img)
{
    cout << "sizeof(img) is: " << sizeof(img) << ", img size is: " << img.size << endl;
    cout << "rows x cols: (" << img.rows << " x " << img.cols << ")" << endl;
    cout << "dims: " << img.dims << endl;
    cout << "channels: " << img.channels() << endl;
    cout << "type: " << img.type() << endl;
    cout << "depth:" << img.depth() << endl;
    cout << "elemSize:" << img.elemSize() << " (Bytes per element)" << endl;
    cout << "elemSize1:" << img.elemSize1() << "(Bytes per channel)" << endl;
    cout << "step[0]: " << img.step[0] << " (Bytes per rows only when 2 dims)" << endl;
    cout <<	"step[1]: " << img.step[1] << " (Bytes per element only when 2 dims)" << endl;
    cout << "step1(0): " << img.step1(0) << ", step1(1): " << img.step1(1) << " (step / elemSize1)" << endl;
}

static void show_mat(const cv::Mat &mat)
{
    uchar *temp = nullptr;
    cout << endl << "***************** show mat ******************" << endl;
    cout << mat << endl;
    show_mat_param(mat);

    for (int r = 0; r < mat.rows; ++r) {
        for (int c = 0; c < mat.cols; ++c) {
            temp = mat.data + mat.step[0] * r + mat.step[1] * c;
            cout << "[ " ;
            for (size_t i = 0; i < mat.step[1]; ++i) {
                cout << (int)*(temp + i) << " ";
            }
            cout << "]" ;
        }
        cout << endl;
    }
}

static void pic2digit(const cv::Mat &mat, const string &filename)
{
    uchar *temp = mat.data;
    ofstream out(filename);

    for (int r = 0; r < mat.rows; ++r) {
        for (int c = 0; c < mat.cols; ++c) {
            temp += mat.step[1];
            for (size_t i = 0; i < mat.step[1]; ++i) {
                out << setw(2) << setfill('0') << (int)*(temp+i) << " ";
            }
        }
        out << endl;
    }
}

static void test_pic2digit()
{
    auto img = cv::imread("/home/jake/Pictures/test.png");
    if (img.empty()) {
        cout << "The picture is not exist!" << endl;
        return;
    }

    vector<cv::Mat> channels;
    cv::split(img, channels);
    pic2digit(channels[0], "digit.txt");
//    int i = 0;
//    for (auto mat : channels) {
//        show(to_string(i++), mat);
//    }
//    show("origin", img);
}

//int main()
//{
////    auto img = cv::imread("/home/jake/Pictures/lena.jpeg");
//    auto img = cv::imread("/home/jake/Pictures/test.png");
//    if (img.empty()) {
//        cout << "The picture is not exist!" << endl;
//        return 1;
//    }

////    show_image_param(img);

////    cv::Mat mat(3, 4, CV_8UC3, cv::Scalar_<uchar>(1, 3));
////    show_mat(mat);

//    cv::waitKey();
//    cv::destroyAllWindows();
//    return 0;
//}

void test_cvtColor(const cv::Mat &img)
{
    cv::Mat gray;
    cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

    show("Origin", img);
    show("Gray", gray);
}

int main( void ){
  char atom_window[] = "Drawing 1: Atom";
  char rook_window[] = "Drawing 2: Rook";
  Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
  Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
  MyEllipse( atom_image, 90 );
  MyEllipse( atom_image, 0 );
  MyEllipse( atom_image, 45 );
  MyEllipse( atom_image, -45 );
  MyFilledCircle( atom_image, Point( w/2, w/2) );
  MyPolygon( rook_image );
  rectangle( rook_image,
         Point( 0, 7*w/8 ),
         Point( w, w),
         Scalar( 0, 255, 255 ),
         FILLED,
         LINE_8 );
  MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
  MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, w ) );
  MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
  MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );
  imshow( atom_window, atom_image );
  moveWindow( atom_window, 0, 200 );
  imshow( rook_window, rook_image );
  moveWindow( rook_window, w, 200 );
  waitKey( 0 );
  return(0);
}
void MyEllipse( Mat img, double angle )
{
  int thickness = 2;
  int lineType = 8;
  ellipse( img,
       Point( w/2, w/2 ),
       Size( w/4, w/16 ),
       angle,
       0,
       360,
       Scalar( 255, 0, 0 ),
       thickness,
       lineType );
}
void MyFilledCircle( Mat img, Point center )
{
  circle( img,
      center,
      w/32,
      Scalar( 0, 0, 255 ),
      FILLED,
      LINE_8 );
}
void MyPolygon( Mat img )
{
  int lineType = LINE_8;
  Point rook_points[1][20];
  rook_points[0][0]  = Point(    w/4,   7*w/8 );
  rook_points[0][1]  = Point(  3*w/4,   7*w/8 );
  rook_points[0][2]  = Point(  3*w/4,  13*w/16 );
  rook_points[0][3]  = Point( 11*w/16, 13*w/16 );
  rook_points[0][4]  = Point( 19*w/32,  3*w/8 );
  rook_points[0][5]  = Point(  3*w/4,   3*w/8 );
  rook_points[0][6]  = Point(  3*w/4,     w/8 );
  rook_points[0][7]  = Point( 26*w/40,    w/8 );
  rook_points[0][8]  = Point( 26*w/40,    w/4 );
  rook_points[0][9]  = Point( 22*w/40,    w/4 );
  rook_points[0][10] = Point( 22*w/40,    w/8 );
  rook_points[0][11] = Point( 18*w/40,    w/8 );
  rook_points[0][12] = Point( 18*w/40,    w/4 );
  rook_points[0][13] = Point( 14*w/40,    w/4 );
  rook_points[0][14] = Point( 14*w/40,    w/8 );
  rook_points[0][15] = Point(    w/4,     w/8 );
  rook_points[0][16] = Point(    w/4,   3*w/8 );
  rook_points[0][17] = Point( 13*w/32,  3*w/8 );
  rook_points[0][18] = Point(  5*w/16, 13*w/16 );
  rook_points[0][19] = Point(    w/4,  13*w/16 );
  const Point* ppt[1] = { rook_points[0] };
  int npt[] = { 20 };
  fillPoly( img,
        ppt,
        npt,
        1,
        Scalar( 255, 255, 255 ),
        lineType );
}
void MyLine( Mat img, Point start, Point end )
{
  int thickness = 2;
  int lineType = LINE_8;
  line( img,
    start,
    end,
    Scalar( 0, 0, 0 ),
    thickness,
    lineType );
}
